﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Utilities.Linq;
using Utilities.Xna.Framework.Graphics;

namespace SceneManager.Processors
{
    public class EffectProcessor : TextureProcessor
    {
        public static Vector2 HalfPixel { get; private set; }

        public Effect Effect { get; set; }

        private bool needHalfPixel;

        public EffectProcessor(Game game, string assetName) : base(game)
        {
            if (HalfPixel == Vector2.Zero)
                HalfPixel = new Vector2(0.5f / (float)Width, 0.5f / (float)Height);

            Effect = Game.Content.Load<Effect>(assetName);
            needHalfPixel = Effect.Parameters.Contains(p => p.Name == "HalfPixel");

            IEnumerable<string> inputs = Effect.Parameters.Where(p => p.ParameterType == EffectParameterType.Texture).Select(p => p.Name);
            foreach (string input in inputs)
                Inputs.Add(input, null);

            IEnumerable<string> outputs = Effect.CurrentTechnique.Annotations.Where(a => a.Name.StartsWith("Output")).Select(a => a.GetValueString());
            if (outputs.Count() == 0)
                Outputs.Add("Output", new RenderTarget2D(Game.GraphicsDevice, Width, Height, false, SurfaceFormat.Color, DepthFormat.None));
            else foreach (string output in outputs)
                Outputs.Add(output, new RenderTarget2D(Game.GraphicsDevice, Width, Height, false, SurfaceFormat.Color, DepthFormat.None));
        }

        public override void Process(Camera camera, GameTime gameTime)
        {
#if DEBUG
            stopwatch.Restart();
#endif

            if (!(this is IFinalRenderer))
            {
                Game.GraphicsDevice.SetRenderTarget(null);
                Game.GraphicsDevice.BlendState = BlendState.Opaque;
            }

            foreach (KeyValuePair<string, Texture2D> input in Inputs)
                Effect.Parameters[input.Key].SetValue(input.Value);


            if (Outputs.Count > 0 && !(this is IFinalRenderer))
            {
                IEnumerable<RenderTargetBinding> renderTargets = Outputs.Values.Select(t => (RenderTargetBinding)t);
                Game.GraphicsDevice.SetRenderTargets(renderTargets.ToArray());
            }

            if (needHalfPixel)
                Effect.Parameters["HalfPixel"].SetValue(HalfPixel);

            foreach (EffectPass pass in Effect.CurrentTechnique.Passes)
            {
                pass.Apply();
                Game.GraphicsDevice.ComputeShader();
            }

#if DEBUG
            processTimes.Enqueue((int)stopwatch.ElapsedTicks);
            while (processTimes.Count > 60)
                processTimes.Dequeue();
#endif
        }
    }
}
